﻿using System;
using System.Collections.Generic;
using System.Linq;

using MvcCms.Data;
using MvcCms.Data.Pagination;
using MvcCmsForum.Data;

using MvcCms.Service.Cache;
using MvcCms.Service.Code;
using MvcCms.Service.Validation;
using MvcCms.Service.Logging;
using Microsoft.Practices.ServiceLocation;

namespace MvcCmsForum.Service
{
    public class ForumService : IForumService
    {
        #region Constructors

        private readonly ICacheService _cacheService;
        private readonly IForumRepository _repository;
        private readonly IValidationDictionary _validationDictionary;

        public ForumService(IValidationDictionary validationDictionary,
                              IForumRepository repository, ICacheService cacheService)
        {
            _validationDictionary = validationDictionary;
            _repository = repository;
            _cacheService = cacheService;
        }

        #endregion

        #region Validation Methods

        private bool ValidateForum(Forum forumToValidate)
        {
            if (forumToValidate.Title.Trim().Length == 0)
                _validationDictionary.AddError("Title", "Title is required.");
            if (forumToValidate.ForumID == 0)
            {
                var forumexists = GetForum(forumToValidate.Path);
                if (forumexists != null)
                    _validationDictionary.AddError("Title", "There is already a forum with this name.");
            }
            return _validationDictionary.IsValid;
        }

        private bool ValidatePost(Post postToValidate)
        {
            if (postToValidate.Body.Trim().Length == 0)
                _validationDictionary.AddError("Body", "Body is required.");
            if (postToValidate.PostID == 0 && postToValidate.ThreadParentPost == null)
            {
                var postexists = GetPost(postToValidate.Path, postToValidate.Forum.ForumID);
                if (postexists != null)
                    _validationDictionary.AddError("Title", "There is already a post in this forum with this name.");
            }
            return _validationDictionary.IsValid;
        }

        #endregion

        #region Forum Methods

        public Forum GetForum(string path)
        {
            return _repository.GetForum(path);
        }
        public Forum GetForum(int id)
        {
            return _repository.GetForum(id);
        }

        public bool CreateForum(Forum forumToCreate)
        {
            // Validation logic
            if (!ValidateForum(forumToCreate))
                return false;

            // Database logic
            try
            {
               
                var portalRepository = ServiceLocator.Current.GetInstance<IPortalRepository>();

                var portal = portalRepository.GetPortal(_cacheService.GetCurrentPortal().PortalID);
                forumToCreate.Portal = portal;
                _repository.CreateForum(forumToCreate);
            }
            catch (Exception ex)
            {
                AppLogging.GetLogger().Error(GeneralMethods.GetStackNames(), ex);
                return false;
            }
            return true;
        }

        public IEnumerable<Forum> ListForums()
        {
             var portalRepository = ServiceLocator.Current.GetInstance<IPortalRepository>();

            var portal = portalRepository.GetPortal(_cacheService.GetCurrentPortal().PortalID);

            return _repository.ListForums(portal.PortalID);
        }

        public bool EditForum(Forum forumToEdit)
        {
            // Validation logic
            if (!ValidateForum(forumToEdit))
                return false;

            // Database logic
            try
            {
                _repository.EditForum(forumToEdit);
            }
            catch (Exception ex)
            {
                AppLogging.GetLogger().Error(GeneralMethods.GetStackNames(), ex);
                return false;
            }
            return true;
        }

        public bool DeleteForum(Forum forumToDelete)
        {
            try
            {
                _repository.DeleteForum(forumToDelete);
            }
            catch (Exception ex)
            {
                AppLogging.GetLogger().Error(GeneralMethods.GetStackNames(), ex);
                return false;
            }
            return true;
        }

        #endregion

        #region Post Methods
        public bool DeletePost(Post postToDelete)
        {
            try
            {
                _repository.DeletePost(postToDelete);
            }
            catch (Exception ex)
            {
                AppLogging.GetLogger().Error(GeneralMethods.GetStackNames(), ex);
                return false;
            }
            return true;
        }

        public Post GetPost(string path, int forumId)
        {
            return _repository.GetPost(path, forumId);
        }

        public Post GetPost(int id)
        {
            return _repository.GetPost(id);
        }

        public DTOAjaxPost GetDTOPost(int id)
        {
            var post = _repository.GetPost(id);

            var newpost = new DTOAjaxPost
            {
                AddedByAvatarUrl = GeneralMethods.GetAddedByAvatarUrl(post.ThreadUser),
                LastPostByAvatarUrl = GeneralMethods.GetAddedByAvatarUrl(post.ReplyUser),
                AddedByIP = post.AddedByIP,
                AddedDate = post.AddedDate,
                Approved = post.Approved,
                Body = post.Body,
                Closed = post.Closed,
                EditDate = post.EditDate,
                LastPostDate = post.LastPostDate,
                Path = post.Path,
                Replies = post.Replies,
                ReplyCount = post.ReplyCount,
                ThreadUser = post.ThreadUser,
                ReplyUser = post.ReplyUser,
                Title = post.Title,
                ViewCount = post.ViewCount,
                Votes = post.Votes,
                VoteCount = post.VoteCount,
                PostID = post.PostID,
                ThreadParentPost = post.ThreadParentPost,
                Forum = post.Forum
            };

            return newpost;
        }

        public IEnumerable<DTOAjaxPost> ListReplies(int threadId, int index)
        {
            const int count = 5;
            var query = _repository.ListReplies(threadId).AsPagination(index, count);
            var totalPages = query.TotalPages;
            var totalItems = query.TotalItems;
            var posts = query.ToList();
            
            IList<DTOAjaxPost> custposts = new List<DTOAjaxPost>();
            foreach (var post in posts)
            {
                var newpost = new DTOAjaxPost
                {
                    PageSize = count,
                    TotalItems = totalItems,
                    PageNumber = index,
                    TotalPages = totalPages,
                    AddedByAvatarUrl = GeneralMethods.GetAddedByAvatarUrl(post.ThreadUser),
                    LastPostByAvatarUrl = GeneralMethods.GetAddedByAvatarUrl(post.ReplyUser),
                    AddedByIP = post.AddedByIP,
                    AddedDate = post.AddedDate,
                    Approved = post.Approved,
                    Body = post.Body,
                    Closed = post.Closed,
                    EditDate = post.EditDate,
                    Forum = post.Forum,
                    LastPostDate = post.LastPostDate,
                    Path = post.Path,
                    Replies = post.Replies,
                    ReplyCount = post.ReplyCount,
                    ThreadParentPost = post.ThreadParentPost,
                    ThreadUser = post.ThreadUser,
                    ReplyUser = post.ReplyUser,
                    Title = post.Title,
                    ViewCount = post.ViewCount,
                    Votes = post.Votes,
                    VoteCount = post.VoteCount,
                    PostID = post.PostID,
                    IsAnswer = post.IsAnswer,
                    IsAnswered = post.IsAnswered
                };

                custposts.Add(newpost);
            }
            return custposts;
        }

        public IEnumerable<DTOAjaxPost> ListPosts(int forumId)
        {
            var posts = _repository.ListPosts(forumId);

            IList<DTOAjaxPost> custposts = new List<DTOAjaxPost>();
            foreach (var post in posts)
            {
                var newpost = new DTOAjaxPost
                {
                    AddedByAvatarUrl = GeneralMethods.GetAddedByAvatarUrl(post.ThreadUser),
                    LastPostByAvatarUrl = GeneralMethods.GetAddedByAvatarUrl(post.ReplyUser),
                    AddedByIP = post.AddedByIP,
                    AddedDate = post.AddedDate,
                    Approved = post.Approved,
                    Body = post.Body,
                    Closed = post.Closed,
                    EditDate = post.EditDate,
                    Forum = post.Forum,
                    LastPostDate = post.LastPostDate,
                    Path = post.Path,
                    Replies = post.Replies,
                    ReplyCount = post.ReplyCount,
                    ThreadParentPost = post.ThreadParentPost,
                    ThreadUser = post.ThreadUser,
                    ReplyUser = post.ReplyUser,
                    Title = post.Title,
                    ViewCount = post.ViewCount,
                    Votes = post.Votes,
                    VoteCount = post.VoteCount,
                    PostID = post.PostID
                };

                custposts.Add(newpost);
            }
            return custposts;
        }

        public IEnumerable<DTOAjaxPost> ListUserPosts(Guid userId)
        {
            IList<DTOAjaxPost> custposts = new List<DTOAjaxPost>();
            try
            {
                var posts = _repository.ListUserPosts(userId)
                    .OrderByDescending(post => post.LastPostDate);

                foreach (var postToUse in posts)
                {
                    Post post;

                    if (postToUse.ReplyCount > 0)
                    {
                        post = _repository.GetLastReply(postToUse.PostID);
                    }
                    else
                    {
                        post = postToUse;
                    }
                    var newpost = new DTOAjaxPost
                    {
                        AddedDate = post.AddedDate,
                        ForumPath = post.Forum.Path,
                        LastPostDate = post.LastPostDate,
                        Path = post.Path,
                        Title = post.Title,
                        VoteCount = post.VoteCount,
                        PostID = post.PostID
                    };

                    if (post.ThreadParentPost == null)
                    {
                        newpost.IsReply = false;
                    }
                    else
                    {
                        if (post.ReplyNumber.HasValue)
                        {
                            int divisor;
                            if (post.ReplyNumber < 7)
                            {
                                divisor = 6;
                            }
                            else
                            {
                                divisor = 5;
                            }
                            int page = (post.ReplyNumber.GetValueOrDefault() / divisor);
                            if (page > 0)
                            {
                                page += 1;
                                newpost.Path = post.ThreadParentPost.Path + "/page" + page;
                            }
                            else
                            {
                                newpost.Path = post.ThreadParentPost.Path;
                            }
                        }
                        else
                        {
                            newpost.Path = post.ThreadParentPost.Path;
                        }
                        newpost.IsReply = true;
                    }
                    custposts.Add(newpost);
                }
                return custposts;
            }
            catch (Exception ex)
            {
                AppLogging.GetLogger().Error(GeneralMethods.GetStackNames(), ex);
                return custposts;
            }
        }

        public IEnumerable<DTOAjaxPost> ListPosts()
        {
            IList<DTOAjaxPost> custposts = new List<DTOAjaxPost>();
            try
            {
                var posts = _repository.ListPortalForumsPosts(_cacheService.GetCurrentPortal().PortalID)
                    .OrderByDescending(post => post.LastPostDate);

                foreach (var postToUse in posts)
                {
                    Post post;

                    if (postToUse.ReplyCount > 0)
                    {
                        post = _repository.GetLastReply(postToUse.PostID);
                    }
                    else
                    {
                        post = postToUse;
                    }
                    var newpost = new DTOAjaxPost
                    {
                        AddedDate = post.AddedDate,
                        ForumPath = post.Forum.Path,
                        LastPostDate = post.LastPostDate,
                        Path = post.Path,
                        Title = post.Title,
                        VoteCount = post.VoteCount,
                        PostID = post.PostID,
                        UserName = post.ThreadUser.UserName.ToProfileLink()
                    };

                    if (post.ThreadParentPost == null)
                    {
                        newpost.IsReply = false;
                    }
                    else
                    {
                        if (post.ReplyNumber.HasValue)
                        {
                            int divisor;
                            if (post.ReplyNumber < 7)
                            {
                                divisor = 6;
                            }
                            else
                            {
                                divisor = 5;
                            }
                            int page = (post.ReplyNumber.GetValueOrDefault() / divisor);
                            if (page > 0)
                            {
                                double rem = post.ReplyNumber.GetValueOrDefault() % divisor;
                                if (rem != 0) { page += 1; }
                                newpost.Path = post.ThreadParentPost.Path + "/page" + page;
                            }
                            else
                            {
                                newpost.Path = post.ThreadParentPost.Path;
                            }
                        }
                        else
                        {
                            newpost.Path = post.ThreadParentPost.Path;
                        }
                        newpost.IsReply = true;
                    }
                    custposts.Add(newpost);
                }
                return custposts;
            }
            catch (Exception ex)
            {
                AppLogging.GetLogger().Error(GeneralMethods.GetStackNames(), ex);
                return custposts;
            }
        }

        public int CreatePost(Post postToCreate)
        {
            // Validation logic
            if (!ValidatePost(postToCreate))
                return 0;

            // Database logic
            try
            {
                postToCreate.IsAnswered = false;
                postToCreate.IsAnswer = false;
                return _repository.CreatePost(postToCreate);
            }
            catch (Exception ex)
            {
                AppLogging.GetLogger().Error(GeneralMethods.GetStackNames(), ex);
                return 0;
            }
        }

        public bool EditPost(Post postToEdit)
        {
            postToEdit.EditDate = DateTime.Now;
            // Validation logic
            if (!ValidatePost(postToEdit))
                return false;

            // Database logic
            try
            {
                _repository.EditPost(postToEdit);
            }
            catch (Exception ex)
            {
                AppLogging.GetLogger().Error(GeneralMethods.GetStackNames(), ex);
                return false;
            }
            return true;
        }

        #endregion

        #region Vote Methods

        public Vote GetVote(int postId, Guid userId)
        {
            return _repository.GetVote(postId, userId);
        }
        public int GetUserVoteTotal(Guid userId)
        {
            return _repository.GetUserVoteTotal(userId);
        }
        #endregion
    }
}